// Package myjwt
// @Author: zhangdi
// @File: jwt
// @Version: 1.0.0
// @Date: 2023/5/4 10:44
package myjwt

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
)

// secretKey  -----自定义
const secretKey = "https://github.com/zhangdi168"
const UserIdFieldName = "userid"

// MyClaims 生成token携带的数据时------自定义
type MyClaims struct {
	Id                   any `json:"id"`
	Username             any `json:"username"`
	jwt.RegisteredClaims     //官方自带字段
}

// UseJwt JWT认证中间件
func UseJwt(c *gin.Context) {
	// 从请求头中获取Token信息
	signToken := c.Request.Header.Get("Authorization")
	signToken = strings.TrimPrefix(signToken, "Bearer ")

	if signToken == "" {
		signToken = c.Param("Authorization")
	}
	errInfo := gin.H{
		"code": 0,
		"msg":  "鉴权失败，请先登录",
	}
	// 解析并验证Token有效性，当校验失败时，响应客户端，并终止后面的处理
	myClaims, err := ParserToken(signToken)
	if err != nil {
		fmt.Println(err)
		c.JSON(http.StatusOK, errInfo)
		c.Abort()
		return
	}
	// 将用户的ID存放到上下文Context中，后续的处理函数中可以通过c.Get("userid")获取当前请求的ID
	c.Set(UserIdFieldName, myClaims.Id)
	c.Next()
}

// getSecretKey 获取密钥
func getSecretKey() []byte {
	return []byte(secretKey)
}

// secretFunc 定义获取密钥的函数
func secretFunc() jwt.Keyfunc {
	// 返回使用getSecretKey()获取到的密钥
	return func(token *jwt.Token) (interface{}, error) {
		return getSecretKey(), nil
	}
}

// GenToken 生成JWT Token
func GenToken(claims MyClaims, timeoutmin time.Duration) (string, error) {
	//系统自带的payload
	sysClaims := jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * timeoutmin)), // 过期时间3小时, //过期时间3小时
		IssuedAt:  jwt.NewNumericDate(time.Now()),                               // 签发时间
		NotBefore: jwt.NewNumericDate(time.Now()),                               // 生效时间
	}

	// 设置Payload数据
	claims.RegisteredClaims = sysClaims

	// 使用HS256加密算法创建Token对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 生成签名字符串
	signToken, err := token.SignedString(getSecretKey())
	if err != nil {
		return "", err
	}
	return signToken, nil
}

// ParserToken 解析JWT Token
func ParserToken(signToken string) (*MyClaims, error) {
	// 使用指定的解析函数解析Token信息
	token, err := jwt.ParseWithClaims(signToken, &MyClaims{}, secretFunc())

	// 当解析出错时，根据错误类型进行不同的处理
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, errors.New("that's not even a token")
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New("token is expired")
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, errors.New("token not active yet")
			} else {
				return nil, errors.New("couldn't handle this token")
			}
		}
	}
	// 解析出了有效的Token数据时，返回解析结果
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("couldn't handle this token")
}
